package gweb

import (
	"net/http"

	"gitee.com/makitdone/gx"
	"gitee.com/makitdone/gx/maps"
	"github.com/gin-gonic/gin"
)

type HandlerFunc func(*Context)

type HttpMethod string

const (
	HttpGET     HttpMethod = "GET"
	HttpPOST    HttpMethod = "POST"
	HttpPUT     HttpMethod = "PUT"
	HttpDELETE  HttpMethod = "DELETE"
	HttpPATCH   HttpMethod = "PATCH"
	HttpOPTIONS HttpMethod = "OPTIONS"
	HttpHEAD    HttpMethod = "HEAD"
)

type Route struct {
	App          *GinApp
	RelativePath string

	routeInfo gx.M
}

func (r *Route) MaintainAllow() {
	r.SetInfo("maintain_allow", true)
}

func (r *Route) IsMaintainAllow() bool {
	return r.routeInfo["maintain_allow"] == true
}

func (r *Route) GetInfo(key string) any {
	return r.routeInfo[key]
}

func (r *Route) SetInfo(key string, value any) *Route {
	r.routeInfo[key] = value
	return r
}

func (r *Route) AddInfo(info gx.M) *Route {
	r.routeInfo = maps.Merge(r.routeInfo, info)
	return r
}

type RouterGroup struct {
	*gin.RouterGroup
	RelativePath string

	App *GinApp
}

func (r *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {
	group := r.RouterGroup.Group(relativePath, wrapHandler(&Route{}, handlers...))
	return &RouterGroup{RouterGroup: group, App: r.App, RelativePath: relativePath}
}

func (r *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.GET(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.POST(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) PUT(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.PUT(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) DELETE(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.DELETE(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) OPTIONS(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.OPTIONS(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) HEAD(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.HEAD(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) PATCH(relativePath string, handlers ...HandlerFunc) *Route {
	route := Route{App: r.App, routeInfo: gx.M{}, RelativePath: relativePath}
	r.RouterGroup.PATCH(relativePath, wrapHandler(&route, handlers...))
	return &route
}

func (r *RouterGroup) AddViews(views ...IRouteView) {
	for _, view := range views {
		view.AddRoutes(r)
	}
}

func (r *RouterGroup) UseMiddleware(middleware ...HandlerFunc) *RouterGroup {
	middlewares := []gin.HandlerFunc{}
	for _, m := range middleware {
		middlewares = append(middlewares, func(ctx *gin.Context) {
			c := NewContext(r.App, ctx)
			m(c)
		})
	}
	r.RouterGroup.Use(middlewares...)
	return r
}

func (r *RouterGroup) Config(relativePath string, fc func(*RouterGroup)) *RouterGroup {
	router := r.Group(relativePath)
	fc(router)
	return router
}

func wrapHandler(route *Route, handlers ...HandlerFunc) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		if handlers == nil {
			return
		}

		c := NewContext(route.App, ctx)
		c.Route = route

		claim := c.GetClaim()
		// 系统维护
		if !route.IsMaintainAllow() && c.App.state == StateMaintain && (claim == nil || !claim.IsSuperuser) {
			c.JSON(http.StatusOK, map[string]any{
				"state":   "maintain",
				"message": "系统维护中...",
			})
			c.Abort()
		}

		// 检查路由版本，如果路由版本不一致，则执行相应事件
		routeVersion := route.routeInfo["version"]
		if routeVersion != nil {
			requestVersion := c.GetHeader("api-version")
			_routeVersion := routeVersion.(string)
			if requestVersion != "" && requestVersion != routeVersion {
				appHandlers := c.App.handlers
				if len(appHandlers) > 0 {
					for _, handler := range appHandlers {
						handler.OnRouteVersionChanged(c, _routeVersion, requestVersion)
					}
				}
			}
		}

		for _, handler := range handlers {
			if c.IsAborted() {
				return // 已中止，返回
			}

			handler(c)
		}
	}
}
